package io.github.wslxm.springbootplus2.manage.sys.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.google.common.base.CaseFormat;
import io.github.wslxm.springbootplus2.client.sys.model.vo.USysDictGroupVO;
import io.github.wslxm.springbootplus2.client.sys.model.vo.USysDictVO;
import io.github.wslxm.springbootplus2.core.base.service.impl.BaseServiceImpl;
import io.github.wslxm.springbootplus2.core.config.error.ErrorException;
import io.github.wslxm.springbootplus2.core.enums.BaseDic;
import io.github.wslxm.springbootplus2.core.result.ResultType;
import io.github.wslxm.springbootplus2.core.utils.XjBeanUtil;
import io.github.wslxm.springbootplus2.core.utils.XjStringUtil;
import io.github.wslxm.springbootplus2.core.utils.tree.XjTreeUtil;
import io.github.wslxm.springbootplus2.core.utils.validated.XjValidUtil;
import io.github.wslxm.springbootplus2.manage.sys.mapper.SysDictMapper;
import io.github.wslxm.springbootplus2.manage.sys.model.dto.SysDictDTO;
import io.github.wslxm.springbootplus2.manage.sys.model.entity.SysDict;
import io.github.wslxm.springbootplus2.manage.sys.model.query.SysDictQuery;
import io.github.wslxm.springbootplus2.manage.sys.model.vo.SysDictVO;
import io.github.wslxm.springbootplus2.manage.sys.service.SysDictService;
import io.github.wslxm.springbootplus2.starter.redis.lock.XjDistributedLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @author wangsong
 */
@Service
@Slf4j
public class SysDictServiceImpl extends BaseServiceImpl<SysDictMapper, SysDict> implements SysDictService {

    /**
     * 父级pid
     */
    private static final String PID = "0";

    @Override
    public List<SysDictVO> tree(SysDictQuery query) {
        // 1、判断 code, 不能传递字符串数字来查询
        String code = query.getCode();
        if (StringUtils.isNotBlank(code)) {
            XjValidUtil.isTrue(XjStringUtil.isInteger(code), "sys.config.code.NotInt");
        }
        List<SysDictVO> dictListVO = null;
        // 处理查询参数
        boolean isDisable = ObjectUtil.defaultIfNull(query.getIsDisable(), true);
        if (StringUtils.isNotBlank(code)) {
            // code 查询
            SysDict dictionary = this.getOne(new LambdaQueryWrapper<SysDict>().select(SysDict::getId)
                    .eq(SysDict::getCode, code)
            );
            XjValidUtil.isTrue(dictionary == null, ResultType.BASE_NO_RESP_DATA);
            List<SysDict> dictList = this.list(new LambdaQueryWrapper<SysDict>()
                    .orderByAsc(SysDict::getSort)
                    .orderByDesc(SysDict::getCreateTime)
                    .eq(SysDict::getPid, dictionary.getId())
                    .eq(!isDisable, SysDict::getDisable, BaseDic.Whether.V0.getValue())
            );
            dictListVO = XjBeanUtil.listVo(dictList, SysDictVO.class);
        } else {
            // tree 查询
            List<SysDict> dictList = this.list(new LambdaQueryWrapper<SysDict>()
                    .orderByAsc(SysDict::getSort)
                    .orderByDesc(SysDict::getCreateTime)
                    .in(SysDict::getType, BaseDic.DictType.V1.getValue(), BaseDic.DictType.V2.getValue())
                    .eq(!isDisable, SysDict::getDisable, BaseDic.Whether.V0.getValue())
            );
            List<SysDictVO> dictionaryVos = XjBeanUtil.listVo(dictList, SysDictVO.class);
            SysDictVO dictionaryVO = XjTreeUtil.nextTree(dictionaryVos, PID);
            dictListVO = dictionaryVO.getChildren();
        }
        return dictListVO;
    }


    @Override
    @XjDistributedLock(lockName = "'xj-sys-dict_'+#dto.code", waitTime = 5L)
    public String insert(SysDictDTO dto) {
        String code = dto.getCode().trim();
        if (StringUtils.isBlank(code)) {
            throw new ErrorException("sys.dict.code.NotBlank");
        }
        // 字典: code 不能重复
        // 字典值: 指定字典下 code 不能重复, A字典下 与 B字典下 的 code 可以重复
        // 在同一个 pid 下 code 不能重复
        this.verifyCodeRepeat(dto.getType(), dto.getPid(), code, null);

        // 添加
        SysDict entity = dto.convert(SysDict.class);
        dto.setCode(code);
        boolean b = this.save(entity);
        return entity.getId();
    }


    @Override
    @XjDistributedLock(lockName = "'xj-sys-dict_'+#dto.code", waitTime = 5L)
    public Boolean upd(String id, SysDictDTO dto) {
        // 因为Code不能重复, 编辑了Code 需单独处理数据
        if (dto.getCode() != null) {
            // 在同一个pid 下code不能重复
            String code = dto.getCode().trim();
            dto.setCode(code);
            this.verifyCodeRepeat(dto.getType(), dto.getPid(), code, id);
        }
        SysDict entity = dto.convert(SysDict.class);
        entity.setId(id);
        return this.updateById(entity);
    }


    /**
     * 部门code重复验证
     *
     * @author wangsong
     * @mail 1720696548@qq.com
     * @date 2022/8/20 0020 14:33 验证
     * @version 1.0.0
     */
    private void verifyCodeRepeat(Integer type, String pid, String code, String excludeId) {
        if (type.equals(BaseDic.DictType.V3.getValue())) {
            long count = this.count(new LambdaQueryWrapper<SysDict>()
                    .and(p -> p.eq(SysDict::getPid, pid).eq(SysDict::getCode, code).ne(excludeId != null, SysDict::getId, excludeId))
                    .or(p -> p.ne(SysDict::getType, BaseDic.DictType.V3.getValue()).eq(SysDict::getCode, code).ne(excludeId != null, SysDict::getId, excludeId))
            );
            XjValidUtil.isTrue(count > 0,  ResultType.BASE_CODE_REPEAT);
        } else {
            long count = this.count(new LambdaQueryWrapper<SysDict>()
                    .eq(SysDict::getCode, code)
                    .ne(excludeId != null, SysDict::getId, excludeId)
            );
            XjValidUtil.isTrue(count > 0, ResultType.BASE_CODE_REPEAT);
        }
    }

    @Override
    public Boolean del(String id) {
        List<SysDict> list = this.list();
        List<SysDictVO> dictionaryVOS = XjBeanUtil.listVo(list, SysDictVO.class);
        List<String> ids = XjTreeUtil.getNextIds(dictionaryVOS, id);
        ids.add(id);
        return this.removeByIds(ids);
    }

    @Override
    public Map<String, List<USysDictGroupVO>> findCodeGroup() {
        List<SysDict> list = this.list(new LambdaQueryWrapper<SysDict>()
                .orderByAsc(SysDict::getSort)
                .orderByDesc(SysDict::getCreateTime)
        );
        Map<String, List<SysDict>> dictByPidMap = list.stream().collect(Collectors.groupingBy(SysDict::getPid));
        List<SysDict> dicts = list.stream().filter(p -> p.getType().equals(BaseDic.DictType.V2.getValue())).collect(Collectors.toList());
        Map<String, List<USysDictGroupVO>> dictMap = new HashMap<>();
        for (SysDict dict : dicts) {
            List<SysDict> dictionaries = dictByPidMap.get(dict.getId());
            List<USysDictGroupVO> dictionaryCodeGroupVos = XjBeanUtil.listVo(dictionaries, USysDictGroupVO.class);
            dictMap.put(dict.getCode(), dictionaryCodeGroupVos);
        }
        return dictMap;
    }


    @Override
    public List<USysDictVO> findByCode(String code) {
        // code 查询
        SysDict dictionary = this.getOne(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCode, code));
        if (dictionary == null) {
            return null;
        }
        List<SysDict> dictList = this.list(new LambdaQueryWrapper<SysDict>()
                .eq(SysDict::getPid, dictionary.getId())
        );
        return XjBeanUtil.listVo(dictList, USysDictVO.class);
    }

    @Override
    public USysDictVO findByCodeNext(String code, String nextCode) {
        List<USysDictVO> dictCategory = this.findByCode(code);
        if (dictCategory == null || dictCategory.isEmpty()) {
            return null;
        }
        Map<String, USysDictVO> dictMap = dictCategory.stream().collect(Collectors.toMap(USysDictVO::getCode, p -> p));
        return dictMap.get(nextCode);
    }


    @Override
    public Map<String, String> generateEnum(String pid) {
        List<SysDict> list = this.list();
        List<SysDictVO> dictionaryVos = XjBeanUtil.listVo(list, SysDictVO.class);
        SysDictVO dictionaryVO = XjTreeUtil.nextTree(dictionaryVos, pid);
        Map<String, String> map = new HashMap<>(2, 1);
        if (dictionaryVO.getType().equals(BaseDic.DictType.V1.getValue())) {
            // 生成小模块的枚举类
            String enumsJava = this.generateEnumJavaByTypeDirectory(dictionaryVO);
            map.put("java", enumsJava);
        } else if (dictionaryVO.getType().equals(BaseDic.DictType.V2.getValue())) {
            // 生成单个字典的枚举类
            String enumsJava = this.generateEnumJavaByTypeDict(dictionaryVO);
            map.put("java", enumsJava);
        }
        // 2、生成js key
        SysDictVO sysDictVO = XjTreeUtil.nextTree(dictionaryVos, "0");
        map.put("js", this.generateEnumJs(sysDictVO));
        return map;
    }


    /**
     * 生成java 代码枚举对象， 请将生成好的代码直接替换到 enums/Enums 小的二级类(指定为2级，每个子模块枚举一个类)
     *
     * @author wangsong
     * @email 1720696548@qq.com
     * @date 2020/8/16 0016 0:10
     * @version 1.0.1
     */
    private String generateEnumJavaByTypeDirectory(SysDictVO dict) {
        String code = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, dict.getCode());
        StringBuilder sb = new StringBuilder();
        sb.append("\nimport com.baomidou.mybatisplus.annotation.IEnum;");
        sb.append("\nimport lombok.AllArgsConstructor;");
        sb.append("\nimport lombok.Getter;\n");
        sb.append("\npublic interface ").append(code).append("{\n");
        // 字段名
        for (SysDictVO dictField : dict.getChildren()) {
            //枚举字典的-枚举属性
            if (dictField.getChildren() == null) {
                continue;
            }
            String mode = XjStringUtil.isInteger(dictField.getChildren().get(0).getCode()) ? "Integer" : "String";
            String moduleName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, dictField.getCode());
            sb.append("\n        /**");
            sb.append("\n         * ").append(dictField.getDesc());
            sb.append("\n         */");
            sb.append("\n        @Getter");
            sb.append("\n        @AllArgsConstructor");
            sb.append("\n        enum ").append(moduleName).append(" implements IEnum<" + mode + "> {");
            for (SysDictVO dictValue : dictField.getChildren()) {
                if (XjStringUtil.isInteger(dictValue.getCode())) {
                    sb.append("\n            V").append(dictValue.getCode()).append("(").append(dictValue.getCode())
                            .append(", \"").append(dictValue.getName()).append("\"),");
                    sb.append("    // ").append(dictValue.getDesc());
                } else {
                    // 变更为驼峰模式
                    String codeStr = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_UNDERSCORE, dictValue.getCode().toUpperCase());
                    codeStr = codeStr.replace("-", "_");
                    sb.append("\n            ").append(codeStr).append("(\"").append(dictValue.getCode())
                            .append("\", \"").append(dictValue.getName()).append("\"),");
                    sb.append("    // ").append(dictValue.getDesc());
                }
            }
            sb.append("\n            ;\n");
            sb.append("            private ").append(mode).append(" value;\n");
            sb.append("            private String desc;\n");
            sb.append("        }\n");
        }
        sb.append("    }\n");
        return sb.toString();
    }

    /**
     * @param dict
     * @return
     */
    private String generateEnumJavaByTypeDict(SysDictVO dict) {
        //枚举字典的-枚举属性 没有下级数据
        XjValidUtil.isTrue(dict.getChildren() == null || dict.getChildren().size() == 0, "dict [" + dict.getCode() + "] there is no sub-level data");
        StringBuilder sb = new StringBuilder();
        sb.append("\nimport com.baomidou.mybatisplus.annotation.IEnum;");
        sb.append("\nimport lombok.AllArgsConstructor;");
        sb.append("\nimport lombok.Getter;\n");
        // 字段名
        String mode = XjStringUtil.isInteger(dict.getChildren().get(0).getCode()) ? "Integer" : "String";
        String moduleName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, dict.getCode());
        sb.append("\n/**");
        sb.append("\n * ").append(dict.getDesc());
        sb.append("\n */");
        sb.append("\n@Getter");
        sb.append("\n@AllArgsConstructor");
        sb.append("\nenum ").append(moduleName).append(" implements IEnum<" + mode + "> {");

        for (SysDictVO dictValue : dict.getChildren()) {
            if (XjStringUtil.isInteger(dictValue.getCode())) {
                sb.append("\n    V").append(dictValue.getCode()).append("(").append(dictValue.getCode())
                        .append(", \"").append(dictValue.getName()).append("\"),");
                sb.append("  // ").append(dictValue.getDesc());
            } else {
                // 变更为驼峰模式
                String codeStr = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_UNDERSCORE, dictValue.getCode().toUpperCase());
                codeStr = codeStr.replace("-", "_");
                sb.append("\n    ").append(codeStr).append("(\"").append(dictValue.getCode())
                        .append("\", \"").append(dictValue.getName()).append("\"),");
                sb.append("  // ").append(dictValue.getDesc());
            }
        }
        sb.append("\n    ;\n");
        sb.append("    private ").append(mode).append(" value;\n");
        sb.append("    private String desc;\n");
        sb.append("}\n");
        return sb.toString();
    }


    /**
     * 拼接js 字典数据-KEY
     *
     * @author wangsong
     * @mail 1720696548@qq.com
     * @date 2020/8/16 0016 0:29
     * @version 1.0.1
     */
    private String generateEnumJs(SysDictVO dict) {
        StringBuilder sb = new StringBuilder();
        //
        sb.append("var Enums = {");
        //模块名
        for (SysDictVO dictModule : dict.getChildren()) {
            sb.append("\n    // ").append(dictModule.getName());
            sb.append("\n    ").append(CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, dictModule.getCode() + ": {"));
            //枚举字典的-枚举名--驼峰模式
            for (SysDictVO dictField : dictModule.getChildren()) {
                String moduleName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, dictField.getCode());
                sb.append("\n        ").append(moduleName).append(" : \"").append(dictField.getCode()).append("\",  // ").append(dictField.getName());
            }
            sb.append("\n    },");
        }
        sb.append("\n};");
        return sb.toString();
    }
}

